#ifndef _MYSQLDYNAMICPOOL_H_
#define _MYSQLDYNAMICPOOL_H_

#include <list>
#include <time.h>
#include <algorithm>
#include <assert.h>
#include "gnmutex.h"
#include "pool_guard.h"
#include <string>
#include <typeinfo>
#include <string.h>

using namespace std;
using namespace GNDP;

#ifndef Macro
#define Macro(T) #T
#endif

// global flag for alloc and free flag.
static const int AllocFlag         = 0xA110C;
static const int FreeFlag          = 0xDEA110C;
static const int max_satisfy_count = 3;

//
// malloc just for c compiler, not only for c++
//
template <typename T, typename Locker = CNonMutex>
class CDynamicPoolEx : public IPoolInterface {
	typedef T CObjType;

	// shell struct derived from CObjType
	struct SShellT : public CObjType {
		int ref;

		// default constructor
		SShellT() : ref(0) {}	

		// variable parameter constructor
		template <typename ...Refer>
		SShellT(Refer&&... oRefer) : ref(0), CObjType(oRefer...) {}

		// default destructor.
		virtual ~SShellT() {}
	};

	// resource info.
	struct SResourceT {
		SShellT *array;
		int      size;
	};

	typedef std::list<SShellT*> CObjectList;
	typedef typename CObjectList::iterator CObjListIter;
	typedef typename CObjectList::const_iterator CObjListConstIter;

	typedef std::list<SResourceT> CResourceList;
	typedef typename CResourceList::iterator CResourceListIter;
	typedef typename CResourceList::const_iterator CResourceListConstIter;

	struct Skiller {
		void operator() (SResourceT &Reource) {
			if (Reource.array) {
				::free(Reource.array);
				Reource.array = NULL;
			}
		}
	};

public:
	explicit CDynamicPoolEx() : 
	    m_init_size(1), 
		m_grow_size(1), 
		m_satisfy_count(0) {
		m_all.clear();
		m_free.clear();
	}

	virtual ~CDynamicPoolEx() {	
		m_locker.Lock();
		m_free.clear();
		m_locker.Unlock();

		// release all resources.
		std::for_each(m_all.begin(), m_all.end(), Skiller());
	}

public:
	// all alloc size
	virtual int GetAllAllocSize() const {
		return m_init_size + (m_all.size() - 1) * m_grow_size;
	}

	// left alloc size
	virtual int GetLeftAllocSize() const {
		return m_free.size();
	}

	// object name
	virtual const char *GetObjectName() const {
#if defined(_WIN32)
        static bool g_HasGet = false;
		static std::string strClassName("");
		if (!g_HasGet) {
			const char *pszClassName = typeid(*this).name();
		    const char *pszStart = strstr(pszClassName, "<");
		    const char *pszNext  = strstr(pszStart, ",");
		    strClassName.assign(pszStart+1, pszNext);
		    g_HasGet = true;
		}
		return strClassName.c_str();
#else
		return typeid(*this).name();
#endif
	}

	// get used size
	virtual int GetUsedSize() const {
		return GetAllAllocSize() - GetLeftAllocSize();
	}

	virtual int GetInitSize() const {
		return m_init_size;
	}

	virtual int GetGrowSize() const {
		return m_grow_size;
	}

	virtual int GetAllocMemorySize() const {
		return GetAllAllocSize() * sizeof(T);
	}

public:
	bool Init(int nInitSize, int nGrowSize) {
		m_init_size = nInitSize;
		m_grow_size = nGrowSize;
		if (0 == m_init_size && 0 == m_grow_size) {
			return false;
		}

		if (m_init_size == 0) m_init_size = nGrowSize;
		if (m_grow_size == 0) m_grow_size = nInitSize;
		if (_Allocate(m_init_size)) {
			CPoolGuard::Instance().Register(this);
			return true;
		}

		return false;
	}

	// Get Object.
	template <typename ...Args>
	CObjType* FetchObj(Args&& ... args) {
		SShellT* poShellT = _Fetch();
		if (!poShellT) return NULL;

		// parameter constructor.
		new (poShellT) SShellT(std::forward<Args>(args)...);
		poShellT->ref = AllocFlag;

		return poShellT;
	}

	// release Object.
	void ReleaseObj(CObjType* poT) {
		if (!poT) return;

		SShellT* poShellT = static_cast<SShellT*>(poT);
		if (!poShellT) return ;
		assert(poShellT->ref == AllocFlag && "reference is not right flag");
		poShellT->ref = FreeFlag;

		// address check.only for window's debug.
#if defined(WIN32) && defined(_DEBUG)
		SResourceT t;
		bool ret = _address_check(poShellT, &t);
		assert(ret && "return address check error");
		if (!ret) return ;
 #endif
		// call deallocate to c++ object.
		poShellT->~SShellT();

		// reallocate pShellT
		m_locker.Lock();
		m_free.push_back(poShellT);
	
		// only for window's debug.
#if defined(WIN32) && defined(_DEBUG)
		 _memory_array_check(&t);
#endif
		m_locker.Unlock();
	}

	// gc check. can call outside as possible.
	void GCCheck() {
		for (CResourceListIter it =  m_all.begin(); it != m_all.end(); it++) {
			_memory_array_check(&*it);
		}
	}

protected:
	void _memory_array_check(const SResourceT *t) {
		int all_size = 0; {// calculate all size.	
			for (CResourceListIter it =  m_all.begin(); it != m_all.end(); it++) {
				all_size += it->size;
			}
		}

		all_size -= m_init_size;
		all_size -= t->size;

		int real_size = int(m_free.size()) - int(t->size);
		if (all_size <= 0 || real_size <= 0) { // not enough now?
			return ;
		}

		const int min_size = 10;
		float percent = float(real_size) / float(all_size);
		if (all_size > min_size && percent > 0.5f) { // over then 50, then release it.		
			if (m_satisfy_count++ >= max_satisfy_count) {
				_dealloc(t);
				m_satisfy_count = 0;
			}
		}
	}

	void _dealloc(const SResourceT *t) {
		CObjListIter *it_array = new CObjListIter[t->size];

		// save should remove iterator.
		int index = 0;
		const SShellT *array = t->array;
		for (int i = 0; i < t->size; i++) {
			CObjListIter it = std::find(m_free.begin(), m_free.end(), &(array[i]));
			if (it == m_free.end()) {
				delete [] it_array;
				return ;
			}
			else {
				it_array[index++] = it;
			}
		}
        
		// == now remove related resource. == 
		// remove from free list.
		for (int i = 0; i < index; i++) {
			m_free.erase(it_array[i]);
		}

		// remove from resource list.
		for (CResourceListIter it = m_all.begin(); it != m_all.end(); it++) {
			if (it->array == t->array && it->size == t->size) {
				m_all.erase(it);
				break;
			}
		}
		::free(t->array);
		delete [] it_array;
	}

	// check poShellT address, and return it's array and array size.
	bool _address_check(SShellT* poShellT, SResourceT *t) const {
		CResourceListConstIter it = m_all.begin();
		for (; it != m_all.end(); it++) {
			// address check, if can not find, then maybe it is an invalid address.
			const SShellT* pArr = it->array;
			if (it->size < 1 || !pArr) {
				continue ;
			}

			if (poShellT >= pArr && poShellT <= &pArr[it->size - 1]) {
				if (t) *t = (*it); // wish to return its pointer?
				break;
			}
		}
		return it != m_all.end();
	}

	bool _Allocate(int nSize) {
		assert(nSize > 0 && "size <= 0");

		// new resource and push back
		SShellT* pstArray = (SShellT*)malloc(sizeof(SShellT) * nSize);
		for (int i = 0; i < nSize; i++) {
			m_free.push_back(&pstArray[i]);
		}

		// push to all list objects
		SResourceT t;
		t.array = pstArray;
		t.size = nSize;
		m_all.push_back(t);

		return true;
	}

	SShellT* _Fetch() {
		// lock it
		m_locker.Lock();

		if (m_free.empty()) { // realloc it.
			if (!_Allocate(m_grow_size)) {
				m_locker.Unlock();
				return NULL;
			}
		}

		// get front resource.
		SShellT* poShellT = m_free.front();
		m_free.pop_front();

		// unlock
		m_locker.Unlock();

		return poShellT;
	}

protected:
	CResourceList m_all;          // all resources
	CObjectList   m_free;         // allocated resource list
	Locker        m_locker;       // allocated resource locker

	int           m_init_size;    // init size
	int           m_grow_size;    // grow size
	int           m_satisfy_count;// satisfy count.
};


#endif